<!DOCTYPE html>
<html lang="zh-CN" class="h-full">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI 对话聊天室</title>
    <!-- 引入 Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        /* 自定义滚动条样式 (可选) */
        ::-webkit-scrollbar {
            width: 6px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb {
            background: #c5c5c5;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background: #a8a8a8;
        }
        /* Inter 字体 */
        body {
            font-family: 'Inter', sans-serif;
        }
    </style>
    <!-- 引入 Inter 字体 -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body class="bg-gray-100 h-full flex items-center justify-center">

<!-- 聊天室主容器 -->
<div class="flex flex-col w-full max-w-2xl h-full md:h-[90vh] md:max-h-[700px] bg-white shadow-2xl rounded-lg overflow-hidden">

    <!-- 头部 -->
    <header class="bg-white border-b border-gray-200 p-4">
        <h1 class="text-xl font-semibold text-gray-800 text-center">AI 助手</h1>
    </header>

    <!-- 消息列表 -->
    <main id="message-list" class="flex-1 overflow-y-auto p-6 space-y-4">
        <!-- 初始欢迎消息 -->
        <div class="flex justify-start">
            <div class="bg-gray-200 text-gray-800 p-3 rounded-lg max-w-[80%]">
                <p>你好！有什么可以帮你的吗？</p>
            </div>
        </div>
        <!-- 消息将在这里动态添加 -->
    </main>

    <!-- 加载中提示 (默认隐藏) -->
    <div id="loading-indicator" class="hidden px-6 pb-2 flex items-center space-x-2">
        <div class="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-600"></div>
        <span class="text-sm text-gray-600">AI 正在思考...</span>
    </div>

    <!-- 错误提示 (默认隐藏) -->
    <div id="error-message" class="hidden px-6 pb-4">
        <p class="text-sm text-red-600 bg-red-100 p-3 rounded-lg">
            连接错误，请检查 API 是否正常运行或刷新页面重试。
        </p>
    </div>

    <!-- 输入区域 -->
    <footer class="bg-white border-t border-gray-200 p-4">
        <div class="flex space-x-3">
            <input
                    type="text"
                    id="message-input"
                    placeholder="输入你的消息..."
                    class="flex-1 border border-gray-300 rounded-lg p-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition"
                    autocomplete="off"
            >
            <button
                    id="send-button"
                    class="bg-blue-600 text-white font-semibold px-5 py-3 rounded-lg text-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-200 shadow-md active:bg-blue-800"
            >
                发送
            </button>
        </div>
    </footer>
</div>

<script>
    document.addEventListener('DOMContentLoaded', () => {
        const messageList = document.getElementById('message-list');
        const messageInput = document.getElementById('message-input');
        const sendButton = document.getElementById('send-button');
        const loadingIndicator = document.getElementById('loading-indicator');
        const errorMessage = document.getElementById('error-message');

        let eventSource = null; // 用于存储 EventSource 实例
        let currentAiMessageElement = null; // 用于存储当前 AI 回复的 DOM 元素

        // 发送消息的处理函数
        const sendMessage = () => {
            const message = messageInput.value.trim();
            if (!message) return; // 不发送空消息

            // 1. 清空输入框并禁用按钮
            messageInput.value = '';
            setLoadingState(true);
            errorMessage.classList.add('hidden'); // 隐藏旧的错误信息

            // 2. 将用户消息添加到 UI
            addMessageToUI('user', message);

            // 3. 创建 AI 回复的占位符
            currentAiMessageElement = addMessageToUI('ai', '');

            // 4. 构建 API URL
            // 注意：请根据您的实际主机和端口修改
            // 这里我们使用相对路径，假设前端和后端在同一域下
            // 如果在不同域，请使用完整 URL，例如: 'http://localhost:8801/api/v1/ollama/generate_stream'
            const model = 'deepseek-r1:1.5b'; // 您可以按需更改模型
            const apiUrl = `/api/v1/ollama/generate_stream?model=${model}&message=${encodeURIComponent(message)}`;

            // 5. 初始化 EventSource
            eventSource = new EventSource(apiUrl);
            let fullResponse = ''; // 存储完整的 AI 回复
            let isStreamFinished = false; // <-- 新增标志：用于跟踪是否是正常结束

            // 6. 监听 message 事件
            eventSource.onmessage = (event) => {
                try {
                    const parsedData = JSON.parse(event.data);

                    if (!parsedData) {
                        console.warn('Received empty data object:', event.data);
                        return;
                    }

                    // --- 处理数组或对象格式 ---
                    // 检查服务器返回的是数组（如初次描述）还是对象（如错误日志）
                    let chatResponse;
                    if (Array.isArray(parsedData) && parsedData.length > 0) {
                        // 格式为： [ {...} ]
                        chatResponse = parsedData[0];
                    } else if (typeof parsedData === 'object' && parsedData !== null && !Array.isArray(parsedData)) {
                        // 格式为： { ... }
                        chatResponse = parsedData;
                    } else {
                        console.warn('Received data in unexpected format:', event.data);
                        return;
                    }

                    if (!chatResponse || !chatResponse.result) {
                        console.warn('Could not extract valid chatResponse from parsed data:', event.data);
                        return;
                    }
                    // --- 修正结束 ---


                    // 根据您最新提供的 JSON，文本内容在 'result.output.text'
                    let content = chatResponse.result?.output?.text;

                    if (content) {
                        fullResponse += content;

                        // --- 修正：更鲁棒的 <think> 过滤逻辑 ---
                        // 1. 移除完整的 <think> 块，包括内容
                        let cleanedResponse = fullResponse.replace(/<think>.*?<\/think>/g, '');

                        // 2. 移除任何残余的、单独的 <think> 或 </think> 标签
                        cleanedResponse = cleanedResponse.replace(/<\/?think>/g, '').trim();

                        // 3. 确保 content 没有被完全过滤空 (例如只发送了 <think>)
                        if (cleanedResponse === currentAiMessageElement.innerText.trim() && content.includes('<think')) {
                            console.log('Filtered AI thinking content:', content);
                            return; // 跳过本次渲染
                        }
                        // --- 修正结束 ---

                        // 使用 innerText 来防止 XSS
                        currentAiMessageElement.innerText = cleanedResponse;
                        scrollToBottom();
                    }

                    // 提取 finishReason
                    // 路径是 chatResponse.result.metadata.finishReason
                    const finishReason = chatResponse.result?.metadata?.finishReason;

                    // 7. 检查是否结束
                    if (finishReason === 'STOP') {
                        console.log('Stream finished.');
                        isStreamFinished = true; // <-- 在关闭前设置标志

                        // 确保移除所有残余的 <think> 标签 (最终清理)
                        if (currentAiMessageElement) {
                            let finalResponse = fullResponse.replace(/<think>.*?<\/think>/g, '');
                            finalResponse = finalResponse.replace(/<\/?think>/g, '').trim();
                            currentAiMessageElement.innerText = finalResponse;
                        }

                        closeEventSource();
                        setLoadingState(false);
                    }

                } catch (e) {
                    console.error('Failed to parse event data:', event.data, e);
                    // 如果解析失败，也显示错误
                    showError('解析服务器数据失败。');
                    closeEventSource();
                    setLoadingState(false);
                }
            };

            // 8. 监听 error 事件
            eventSource.onerror = (err) => {
                // 检查是否是正常结束，或者是否已经有内容被渲染（表明连接最初是成功的）
                const hasRenderedContent = currentAiMessageElement && currentAiMessageElement.innerText.trim() !== '...';

                if (isStreamFinished || hasRenderedContent) {
                    console.log('EventSource error ignored (stream finished normally or some content rendered).');
                    // 即使是正常关闭，也要确保 UI 状态正确
                    closeEventSource();
                    setLoadingState(false);
                    return; // 忽略错误提示
                }

                // 真正的连接错误
                console.error('EventSource failed:', err);
                showError('连接 AI 服务失败。请检查后端服务是否正在运行。');
                closeEventSource();
                setLoadingState(false);
            };
        };

        // 将消息添加到 UI 的辅助函数
        const addMessageToUI = (sender, text) => {
            const messageDiv = document.createElement('div');
            messageDiv.className = 'flex'; // 容器

            const textBubble = document.createElement('div');
            // 使用 innerText 来设置内容，确保安全
            textBubble.innerText = text;
            textBubble.className = 'p-3 rounded-lg max-w-[80%] shadow-sm';

            if (sender === 'user') {
                messageDiv.classList.add('justify-end');
                textBubble.classList.add('bg-blue-500', 'text-white');
            } else {
                messageDiv.classList.add('justify-start');
                textBubble.classList.add('bg-gray-200', 'text-gray-800');
                if (text === '') {
                    // 这是 AI 的占位符，添加一个跳动动画
                    textBubble.innerHTML = '<span class="inline-block animate-bounce">...</span>';
                }
            }

            messageDiv.appendChild(textBubble);
            messageList.appendChild(messageDiv);

            scrollToBottom();
            return textBubble; // 返回气泡元素以便后续更新
        };

        // 设置加载状态
        const setLoadingState = (isLoading) => {
            if (isLoading) {
                loadingIndicator.classList.remove('hidden');
                sendButton.disabled = true;
                messageInput.disabled = true;
                sendButton.classList.add('opacity-50', 'cursor-not-allowed');
            } else {
                loadingIndicator.classList.add('hidden');
                sendButton.disabled = false;
                messageInput.disabled = false;
                sendButton.classList.remove('opacity-50', 'cursor-not-allowed');
                messageInput.focus(); // 结束后让输入框重新获得焦点
            }
        };

        // 显示错误信息
        const showError = (message) => {
            errorMessage.innerText = message;
            errorMessage.classList.remove('hidden');
            // 如果 AI 占位符是空的，就移除它
            if (currentAiMessageElement && currentAiMessageElement.innerText === '...') {
                currentAiMessageElement.parentElement.remove();
            }
        };

        // 关闭 EventSource 连接
        const closeEventSource = () => {
            if (eventSource) {
                eventSource.close();
                eventSource = null;
                console.log('EventSource closed.');
            }
        };

        // 滚动到底部
        const scrollToBottom = () => {
            messageList.scrollTop = messageList.scrollHeight;
        };

        // 绑定事件
        sendButton.addEventListener('click', sendMessage);
        messageInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault(); // 阻止换行
                sendMessage();
            }
        });

    });
</script>

</body>
</html>
